package interfaz2;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Date;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;

import core.Util;
import dustconf.DustConf;

/** TODO: Right now when a log line its bigger than the screen (the field
 *  information is quite large), it is not possible to scroll horizontally
 *  in order to read the whole message. We need to make appear the horizontal
 *  scrollbar. **/
/** TODO: Here we have a bug. When Dust is started and immediately closed,
 *  before the updating threads run at start finished the updates, an
 *  SWTException will be thrown and Dust will never get closed. It must be
 *  killed by the system (kill -9, ctrl + alt + supr...). This is due to my
 *  bad practice programming with SWT, I've never checked if the widget was
 *  disposed with the isDisposed() method. Sorry. **/
/**
 * The view of the Log option. It is quite simple, the only module that should
 * use the addLog method is the LogController module.
 * 
 * @author ole
 */
public class LogView extends DustComposite {
	// Class constants.
	private final int TIMESTAMP_WIDTH = 207;
	
	// Class attributes.
	private Table log_table;
	
	/*
	 * Private classes.
	 */
	
	/**
	 * Class that handles the selection of the "Clean Log" button.
	 */
	private class CleanSelectionListener extends SelectionAdapter {
		@Override
		public void widgetSelected(SelectionEvent event) {
			log_table.removeAll();
		}
	}
	
	/**
	 * Class that handles the selection of the "Export Log" button.
	 */
	private class ExportSelectionListener extends SelectionAdapter {
		@Override
		public void widgetSelected(SelectionEvent event) {
			FileDialog fdialog;
			String fname;
			FileOutputStream fos;
			
			fdialog = new FileDialog(getShell());
			fname = fdialog.open();
			
			// Check if the dialog was canceled or if there were errors.
			try {
				if (fname != null) {
					fos = new FileOutputStream(fname);
					writeLog(fos);
				}
			}
			catch (Exception e) {
				ExceptionBox ebox;
				ebox = new ExceptionBox(e, getShell());
				ebox.popError();
			}
		}
	}
	
	/**
	 * Class that will write a log register in the table. This work is done
	 * this way because we want to execute it in the UI thread, so we need
	 * to implement it in the run method of a Runnable implementation in
	 * order to execute it with getDisplay().asynExec(Runnable) method.
	 * 
	 * @author ole
	 */
	private class Writer implements Runnable {
		private String msg;
		
		public Writer(String msg) {
			this.msg = msg;
		}
		
		/**
		 * Create the new register of log inside the table.
		 */
		@Override
		public void run() {
			Date date;
			TableItem item;
			String column_content[];
			
			// Get the actual date.
			date = new Date();
			
			// Create a new item inside the table at the top, add the date to
			// the first column and the message to the second.
			item = new TableItem(log_table, SWT.NONE, 0);
			column_content = new String[2];
			column_content[0] = date.toString();
			column_content[1] = msg;
			item.setText(column_content);
			
			// Makes the view to show the top item of the table.
			log_table.setTopIndex(0);
		}
	}
	
	/*
	 * Private methods of LogView class.
	 */
	
	/**
	 * Writes the actual log to the specified FileOutputStream.
	 * 
	 * @param fos:
	 * 		The FileOutputStream where write to.
	 */
	private void writeLog(FileOutputStream fos) {
		String str;
		
		for (TableItem item: log_table.getItems()) {
			// Get the timestamp.
			str = "[" + item.getText(0) + "]: ";
			
			// Get the message.
			str += item.getText(1) + "\r\n";
			
			try {
				fos.write(str.getBytes());
			}
			// If there are problems writing continue silently...
			catch (IOException e) {
				continue;
			}
		}
	}

	public LogView(Composite parent, int style) throws FileNotFoundException {
		super(parent, style);
		
		// A log composite is splitted into two main zones, a top zone with
		// buttons with options and a bottom zone that is a table with the
		// lines of log.
		Composite buttons_comp;
		Button clean_button, export_button;
		TableColumn log_column;
		GridData grid;
		String images_path;
		
		images_path = Util.checkDirectoryPath(DustConf.getImagesFolder());
		
		this.setLayout(new GridLayout());
		grid = new GridData(GridData.FILL, GridData.FILL, true, true);
		this.setLayoutData(grid);
		
		// A composite for the top buttons.
		buttons_comp = new Composite(this, SWT.NONE);
		buttons_comp.setLayout(new FillLayout());
		
		// A button to clean the actual log.
		clean_button = new Button(buttons_comp, SWT.NONE);
		clean_button.setText("Clean log");
		clean_button.setImage(IfaceUtils.buildImage(images_path + "clean_log.png"));
		clean_button.addSelectionListener(new CleanSelectionListener());
		
		// A button to export the log into a file.
		export_button = new Button(buttons_comp, SWT.NONE);
		export_button.setText("Export log");
		export_button.setImage(IfaceUtils.buildImage(images_path + "export_log.png"));
		export_button.addSelectionListener(new ExportSelectionListener());
		
		// A table with the log lines.
		log_table = new Table(this, SWT.H_SCROLL | SWT.V_SCROLL);
		grid = new GridData(GridData.FILL, GridData.FILL, true, true);
		log_table.setLayoutData(grid);
		log_column = new TableColumn(log_table, SWT.NONE);
		log_column.setText("Timestamp");
		log_column.setWidth(TIMESTAMP_WIDTH);
		log_column = new TableColumn(log_table, SWT.NONE);
		log_column.setText("Message");
		log_column.pack();
		log_table.setHeaderVisible(true);
	}
	
	/**
	 * This will be the method use by the controller to write new logs. Check
	 * the Writer class documentation to know why this is done this way.
	 * 
	 * @param msg:
	 * 		The message to be logged.
	 */
	public void addLog(String msg) {
		Writer writer = new Writer(msg);
		
		if (!this.isDisposed())
			getDisplay().asyncExec(writer);
	}
}
